package codeGeneration;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Properties;

import util.Common;
import util.FileUtil;
import util.HeaderOfJavaFile;

import lexer.TokenConstant;

public class ValidatorGenerator extends Generator {
	private List<Event> events;
	private String fileLocation;
	private String defaultObjName;
	private String defaultClassName;
	private String packageName;
	private String beanName;
	private HeaderOfJavaFile headerOfJavaFile;
	protected StringBuffer sbuffWriter;
	private String messageFileLocation;
	private Properties propOfMessages;
	private FileWriter messageFileWriter;

	public ValidatorGenerator(String basePath, String beanName,
			String packageName, String messagePath, List<Event> events) {
		super();
		this.events = events;
		this.defaultClassName = beanName + "Validator";
		this.packageName = packageName;
		this.beanName = beanName;
		this.defaultObjName = ""
				+ Common.convertFirstLetterToLowerCase(beanName) + "Validator";
		this.fileLocation = basePath + '/' + packageName.replace('.', '/')
				+ '/' + defaultClassName + ".java";
		this.messageFileLocation = messagePath + "/messages.properties";

		File validatorDir = new File(basePath + '/'
				+ packageName.replace('.', '/'));
		if (!validatorDir.exists())
			validatorDir.mkdir();

		File messageDir = new File(messagePath);
		System.out.println(messagePath);
		FileUtil.mkDir(messageDir.getAbsolutePath());

		propOfMessages = new Properties();

		sbuffWriter = new StringBuffer();
		headerOfJavaFile = new HeaderOfJavaFile();
		indentOneTime(HeaderOfJavaFile.packageNamePlaceHolder, null, ";\n",
				sbuffWriter, true);
		indentOneTime(HeaderOfJavaFile.importsListPlaceHolder, null, "",
				sbuffWriter, true);
	}

	public void preloadMessage() throws IOException {
		File messageFile = new File(messageFileLocation);
		if (messageFile.exists())
			return;

		try {
			propOfMessages.load(new FileInputStream(messageFile));
		} catch (IOException e) {
			e.printStackTrace();
			throw e;
		}
	}

	public void generateValidator() throws IOException {
		generateClassBean();
		replacePlaceHolder(sbuffWriter, headerOfJavaFile);

		FileWriter fileWriter = null;
		try {
			fileWriter = getFileWriter(fileLocation);
			fileWriter.append(sbuffWriter);
		} catch (IOException e) {
			e.printStackTrace();
			throw new IOException("Generate validator failed. "
					+ "Can't interact with validator file");
		}

		try {
			messageFileWriter = getFileWriter(messageFileLocation);
			GenerateSetOfRule generateSetOfRule = GenerateSetOfRule
					.getSetOfRule();
			generateSetOfRule.generateMessages(messageFileWriter,
					propOfMessages);
		} catch (IOException e) {
			e.printStackTrace();
			throw new IOException("Generate validator failed. "
					+ "Can't interact with message.properties file");
		}

		closeFileWriter(messageFileWriter);
		closeFileWriter(fileWriter);
	}

	private void generateClassBean() {
		headerOfJavaFile.setPackageName(packageName);
		indentOneTime("public class " + defaultClassName
				+ " implements Validator", null, " {", sbuffWriter, false);
		headerOfJavaFile
				.addImportsList("org.springframework.validation.Validator");

		generateSupportMethod();
		generateValidate();

		decreaseIndentOneTime("}", sbuffWriter);
	}

	private void generateSupportMethod() {
		indentOneTime("public boolean supports(Class<?> clazz)", null, " {",
				sbuffWriter, false);
		appendToFile(sbuffWriter, "return " + defaultClassName
				+ ".class.isAssignableFrom(clazz);\n");

		decreaseIndentOneTime("}\n", sbuffWriter);
	}

	private void generateValidate() {
		indentOneTime("public void validate (Object target, Errors errors)",
				null, " {", sbuffWriter, false);
		headerOfJavaFile
				.addImportsList("org.springframework.validation.Errors");

		headerOfJavaFile.addImportsList(packageName.subSequence(0,
				packageName.lastIndexOf('.'))
				+ ".bean." + beanName);

		appendToFile(
				sbuffWriter,
				beanName + ' ' + ((char) (beanName.charAt(0) + 32))
						+ beanName.substring(1, beanName.length()));
		sbuffWriter.append(" = (" + beanName + ") target;\n");
		generateEvent();

		decreaseIndentOneTime("}", sbuffWriter);
	}

	private void generateEvent() {
		for (Event event : events) {
			if (event.getControlRules().size() > 0) {
				List<ControlRules> controlRuless = event.getControlRules();

				for (ControlRules controlRules : controlRuless) {
					GenerateSetOfRule sor = GenerateSetOfRule.getSetOfRule();
					for (Rule rule : controlRules.getRules()) {
						switch (rule.getTypeCheck()) {
						case TokenConstant.CHECKLENGTH:
							sbuffWriter.append(sor.generateChecklength(spaces,
									indentIndex, beanName,
									controlRules.getControlName(),
									controlRules.getTypeOfControl(),
									rule.getValue1(), rule.getValue2(),
									propOfMessages));
							break;

						case TokenConstant.CHECKRANGE:
							sbuffWriter.append(sor.generateCheckRange(spaces,
									indentIndex, beanName,
									controlRules.getControlName(),
									controlRules.getTypeOfControl(),
									rule.getValue1(), rule.getValue2(),
									propOfMessages));
							break;

						case TokenConstant.CHECKNUMERIC:
						case TokenConstant.CHECKALPHA:
						case TokenConstant.CHECKEMAIL:
						case TokenConstant.CHECKEMPTY:
						case TokenConstant.CHECKPHONENUMBER:
							headerOfJavaFile
									.addImportsList("java.util.regex.Pattern");
							sbuffWriter.append(sor.generateCheckGeneral(spaces,
									indentIndex, beanName,
									controlRules.getControlName(),
									rule.getTypeCheck(), propOfMessages));
							break;
						}
					}
				}
			}
		}
	}

	public String getFileLocation() {
		return fileLocation;
	}

	public void setFileLocation(String fileLocation) {
		this.fileLocation = fileLocation;
	}

	public String getDefaultObjName() {
		return defaultObjName;
	}

	public void setDefaultObjName(String defaultObjName) {
		this.defaultObjName = defaultObjName;
	}

	public String getDefaultClassName() {
		return defaultClassName;
	}

	public void setDefaultClassName(String defaultClassName) {
		this.defaultClassName = defaultClassName;
	}
}